﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.ComponentModel;
using System.Windows;
using System.Diagnostics;

namespace PetriNetSharp.Engine
{
	/// <summary>
	/// An object that is in the petri net graph
	/// </summary>
	public class PetriObject : INotifyPropertyChanged, Iinvalidatable
	{
		/// <summary>
		/// Occurs when the location changes.
		/// </summary>
		public event Action LocationChanged;

		/// <summary>
		/// Occurs when the name changes
		/// </summary>
		public event Action NameChanged;

		/// <summary>
		/// Occurs when IsRunning is changed to a different value
		/// </summary>
		public event Action IsRunningChanged;


		/// <summary>
		/// The name of the PetriObject
		/// </summary>
		private String name;

		/// <summary>
		/// The unique ID of this PetriObject
		/// </summary>
		private String uid;

		/// <summary>
		/// The location of the PetriObject
		/// </summary>
		private Point location;

		/// <summary>
		/// Whether this PetriNetObject is running
		/// </summary>
		private bool isRunning;

		/// <summary>
		/// Create a new name object
		/// </summary>
		/// <param name="name">The name of the PetriObject</param>
		/// <param name="location">The location of the PetriObject</param>
		/// <param name="uid">The unique ID of the PetriObject</param>
		public PetriObject(String name, Point location, string uid)
		{
			this.name = name;
			this.location = location;
			this.uid = uid;
			isRunning = false;
		}

		/// <summary>
		/// Get or set the name of this PetriObject
		/// </summary>
		public String Name
		{
			get { return name; }
			set
			{
				name = value;
				RaisePropertyChanged("Name");
				if(NameChanged != null)
					NameChanged();
			}
		}

		/// <summary>
		/// Get or set the location of this PetriObject
		/// </summary>
		public Point Location
		{
			get { return location; }
			set
			{
				location = value;
				RaisePropertyChanged("Location");
				if ( LocationChanged != null )
					LocationChanged();
			}
		}

		/// <summary>
		/// Get the unique ID of this PetriObject
		/// </summary>
		public string UID { get { return uid; } }

	

		/// <summary>
		/// Get whether this plae is running
		/// </summary>
		public virtual bool IsRunning
		{
			get { return isRunning; }
			protected set
			{
				if (value != IsRunning)
				{
					isRunning = value;
					RaisePropertyChanged("IsRunning");
					if (IsRunningChanged != null)
						IsRunningChanged();
				}
			}
		}

		/// <summary>
		/// Start running
		/// </summary>
		/// <preconditions>
		/// IsRunning must be false
		/// </preconditions>
		public void Start()
		{
			#region preconditions
			Debug.Assert(!IsRunning, String.Format("PetriObject: {0} had Start called when it was already running", Name),
				String.Format("PetriObject: {0} had Start() called when IsRunning was true", Name));
			#endregion

			IsRunning = true;
		}

		/// <summary>
		/// Stop running
		/// </summary>
		/// <preconditions>
		/// IsRunning must be true
		/// </preconditions>
		public void Stop()
		{
			#region preconditions
			Debug.Assert(IsRunning, String.Format("PetriObject: {0} had Stop called when it was not running", Name),
				String.Format("PetriObject: {0} had Stop() called when IsRunning was false", Name));
			#endregion

			IsRunning = false;
		}


		#region INotifyPropertyChanged implemented

		/// <summary>
		/// Occurs when a property value changes.
		/// </summary>
		public event PropertyChangedEventHandler PropertyChanged;

		/// <summary>
		/// Raise the PropertyChanged event, for the passed property name
		/// </summary>
		/// <param name="propertyName">The property that changed</param>
		protected void RaisePropertyChanged(string propertyName)
		{
			if (PropertyChanged != null)
			{
				PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
			}
		}

		#endregion


		#region Object method overrides

		/// <summary>
		/// Determines whether the passed object is equal to this PetriObject
		/// </summary>
		/// <param name="obj">The object to compare</param>
		/// <returns>True if it is equal, false if not</returns>
		public override bool Equals(object obj)
		{
			if (obj is PetriObject)
			{
				PetriObject compare = obj as PetriObject;
				if(UID.Equals(compare.UID))
					return true;
			}
			return false;
		}

		/// <summary>
		/// Serves as a hash function for this type
		/// </summary>
		/// <returns>A hash code for the current PetriObject</returns>
		public override int GetHashCode()
		{
			return base.GetHashCode();
		}

		/// <summary>
		/// Returns a System.String that represents the current PetriObject
		/// </summary>
		/// <returns>A System.String that represents the current PetriObject</returns>
		public override string ToString()
		{
			return String.Format("{0} Location:{1},{2}", Name, Location.X, Location.Y);
		}

		#endregion


		#region Iinvalidatable Members

		/// <summary>
		/// Occurs when this PetriNetOjbect is invalidated
		/// </summary>
		public event Action Invalidated;

		/// <summary>
		/// Invalidate this PetriNetObject
		/// </summary>
		public void Invalidate()
		{
			if (Invalidated != null)
				Invalidated();
		}

		#endregion
	}
}
